/*
 * Copyright (c) 2011-2013, Peter Abeles. All Rights Reserved.
 *
 * This file is part of BoofCV (http://boofcv.org).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boofcv.alg.feature.disparity.impl;

import boofcv.alg.feature.disparity.SelectRectStandard;
import boofcv.struct.image.ImageSingleBand;

/**
 * <p>
 * Implementation of {@link SelectRectStandard} as a base class for arrays of type F32.
 * Extend for different output image types.
 * </p>
 *
 * <p>
 * DO NOT MODIFY. Generated by {@link GenerateSelectRectStandardBase}.
 * </p>
 *
 * @author Peter Abeles
 */
public abstract class ImplSelectRectStandardBase_F32<T extends ImageSingleBand>
		extends SelectRectStandard<float[],T>
{
	// scores organized for more efficient processing
	float columnScore[] = new float[1];
	int imageWidth;

	// texture threshold, use an integer value for speed.
	protected float textureThreshold;

	public ImplSelectRectStandardBase_F32(int maxError, int rightToLeftTolerance, double texture) {
		super(maxError,rightToLeftTolerance,texture);
	}

	@Override
	public void setTexture(double threshold) {
		textureThreshold = (float)threshold;
	}

	@Override
	public void configure(T imageDisparity, int minDisparity, int maxDisparity , int radiusX ) {
		super.configure(imageDisparity,minDisparity,maxDisparity,radiusX);

		if( columnScore.length < maxDisparity )
			columnScore = new float[maxDisparity];
		imageWidth = imageDisparity.width;
	}

	@Override
	public void process(int row, float[] scores ) {

		int indexDisparity = imageDisparity.startIndex + row*imageDisparity.stride + radiusX + minDisparity;

		for( int col = minDisparity; col <= imageWidth-regionWidth; col++ ) {
			// Determine the number of disparities that can be considered at this column
			// make sure the disparity search doesn't go outside the image border
			localMax = maxDisparityAtColumnL2R(col);

			// index of the element being examined in the score array
			int indexScore = col - minDisparity;

			// select the best disparity
			int bestDisparity = 0;
			float scoreBest = columnScore[0] = scores[indexScore];
			indexScore += imageWidth;

			for( int i = 1; i < localMax; i++ ,indexScore += imageWidth) {
				float s = scores[indexScore];
				columnScore[i] = s;
				if( s < scoreBest ) {
					scoreBest = s;
					bestDisparity = i;
				}
			}

			// detect bad matches
			if( scoreBest > maxError ) {
				// make sure the error isn't too large
				bestDisparity = invalidDisparity;
			} else if( rightToLeftTolerance >= 0 ) {
				// if the associate is different going the other direction it is probably noise

				int disparityRtoL = selectRightToLeft(col-bestDisparity-minDisparity,scores);

				if( Math.abs(disparityRtoL-bestDisparity) > rightToLeftTolerance ) {
					bestDisparity = invalidDisparity;
				}
			}
			// test to see if the region lacks sufficient texture if:
			// 1) not already eliminated 2) sufficient disparities to check, 3) it's activated
			if( textureThreshold > 0 && bestDisparity != invalidDisparity && localMax >= 3 ) {
				// find the second best disparity value and exclude its neighbors
				float secondBest = Float.MAX_VALUE;
				for( int i = 0; i < bestDisparity-1; i++ ) {
					if( columnScore[i] < secondBest ) {
						secondBest = columnScore[i];
					}
				}
				for( int i = bestDisparity+2; i < localMax; i++ ) {
					if( columnScore[i] < secondBest ) {
						secondBest = columnScore[i];
					}
				}

				// similar scores indicate lack of texture
				// C = (C2-C1)/C1
				if( secondBest-scoreBest <= textureThreshold*scoreBest )
					bestDisparity = invalidDisparity;
			}

			setDisparity(indexDisparity++ , bestDisparity );
		}
	}

	/**
	 * Finds the best disparity going from right to left image.
	 *
	 */
	private int selectRightToLeft( int col , float[] scores ) {
		// see how far it can search
		int localMax = Math.min(imageWidth-regionWidth,col+maxDisparity)-col-minDisparity;

		int indexBest = 0;
		int indexScore = col;
		float scoreBest = scores[col];
		indexScore += imageWidth+1;

		for( int i = 1; i < localMax; i++ ,indexScore += imageWidth+1) {
			float s = scores[indexScore];

			if( s < scoreBest ) {
				scoreBest = s;
				indexBest = i;
			}
		}

		return indexBest;
	}

}
